/*
 * Copyright (C) 2008 GeoScheduler Team, as stated on <http://www.geoscheduler.org/authors>.
 * 
 * This file is part of GeoScheduler.
 * 
 * GeoScheduler is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * GeoScheduler is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with GeoScheduler. If not, see <http://www.gnu.org/licenses/>.
 */
package org.geoscheduler.commons.qbuilder;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * 
 * FIXME [lko] comment me!
 *
 * @author lko
 */
public abstract class TableDecl<T> {

	private final List<ColumnDecl<T, ?>> keys=new LinkedList<ColumnDecl<T,?>>();
	private final List<ColumnDecl<T, ?>> pkeys = new LinkedList<ColumnDecl<T, ?>>();
	private final List<ColumnDecl<T, ?>> nonpkeys = new LinkedList<ColumnDecl<T, ?>>();

	private final String name;
	private final Class<T> type;

	protected TableDecl(String name, Class<T> type) {
		this.name = name;
		this.type = type;
	}

	public static abstract class ColumnDecl<T, V> {

		private final String name;
		private final boolean pk;
		private final TableDecl<T> table;
		private final Class<V> type;

		public ColumnDecl(TableDecl<T> table, String name, Class<V> type) {
			this(table, name, type, false);
		}

		public ColumnDecl(TableDecl<T> table, String name, Class<V> type, boolean pk) {
			this.name = name;
			this.pk = pk;
			this.table = table;
			this.type = type;
			if (pk) {
				table.pkeys.add(this);
			} else {
				table.nonpkeys.add(this);
			}
			table.keys.add(this);
		}

		public String name() {
			return name;
		}

		public boolean isPK() {
			return pk;
		}

		public String table() {
			return table.name();
		}

		public TableDecl<T> tableDecl() {
			return table;
		}

		public Class<V> type() {
			return this.type;
		}

		public abstract void setValue(T instance, ResultSet rs) throws SQLException;

		public abstract V getValue(T instance);

		@Override
		public String toString() {
			return this.name;
		}
	}

	public static Map<String, String> map(ColumnDecl<?, ?>... decls) {
		return map(Arrays.asList(decls));
	}

	public static Map<String, String> map(List<ColumnDecl<?, ?>> decls) {
		Map<String, String> ret = new LinkedHashMap<String, String>(decls.size());
		for (ColumnDecl<?, ?> decl : decls) {
			ret.put(decl.name(), decl.name());
		}
		return ret;
	}


	public static <T> T setValues(T instance, ResultSet rs, ColumnDecl<T,?>... decls) throws SQLException {
		for (ColumnDecl<T,?> decl : decls) {
			decl.setValue(instance, rs);
		}
		return instance;
	}

	public static <T,V> Map<ColumnDecl<T, ?>, ?> getValues(T instance, ColumnDecl<T, ? extends V>... decls) {
		Map<ColumnDecl<T, ?>, Object> map = new LinkedHashMap<ColumnDecl<T, ?>, Object>();
		for (ColumnDecl<T, ?> decl : decls) {
			map.put(decl, decl.getValue(instance));
		}
		return map;
	}

	public static <T> Map<String, ?> mapValues(T instance, ColumnDecl<T,?>... decls) {
		Map<String, Object> map = new LinkedHashMap<String, Object>(decls.length);
		for (ColumnDecl<T, ?> decl : decls) {
			map.put(decl.name(), decl.getValue(instance));
		}
		return map;
	}
	
	public final List<ColumnDecl<T, ?>> NONPkeys() {
		return Collections.unmodifiableList(nonpkeys);
	}
	
	public final List<ColumnDecl<T, ?>> AllKeys(){
		return Collections.unmodifiableList(keys);
	}

	public List<ColumnDecl<T, ?>> PKeys() {
		return Collections.unmodifiableList(pkeys);
	}

	public Class<T> type() {
		return type;
	}

	public String name() {
		return this.name;
	}
}
